package org.jing.ext.jdbc.comm;

import org.jing.core.lang.JingException;
import org.jing.core.logger.JingLogger;
import org.jing.core.util.ClassUtil;
import org.jing.core.util.GenericUtil;
import org.jing.ext.jdbc.JingJdbcException;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2021-08-25 <br>
 */
public class CommJdbc {
    private static final JingLogger LOGGER = JingLogger.getLogger(CommJdbc.class);

    public static long batchUpdate(Connection connection, String sql, List<Object[]> paramsList) throws JingJdbcException {
        return batchUpdate(connection, sql, paramsList, 10000);
    }

    public static long batchUpdate(Connection connection, String sql, List<Object[]> paramsList, int perBatchNum) throws JingJdbcException {
        LOGGER.debug("[batch] {}", sql);
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            int size = GenericUtil.count(paramsList);
            Object[] params;
            long sum = 0;
            for (int i$ = 0; i$ < size; i$++) {
                params = paramsList.get(i$);
                setParameters(ps, params);
                ps.addBatch();
                if ((i$ + 1) % perBatchNum == 0) {
                    sum += countSumAffectedRow(ps.executeBatch());
                    ps.clearBatch();
                }
            }
            if (size % perBatchNum != 0) {
                sum += countSumAffectedRow(ps.executeBatch());
                ps.clearBatch();
            }
            LOGGER.debug("{} rows affected", sum);
            return sum;
        }
        catch (SQLException e) {
            throw new JingJdbcException(e);
        }
    }

    public static int update(Connection connection, String sql, Object... params) throws JingJdbcException {
        return update(connection, true, sql, params);
    }

    public static int update(Connection connection, boolean log, String sql, Object... params) throws JingJdbcException {
        if (log) {
            LOGGER.debug("[update] {}", sql);
        }
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            setParameters(ps, params);
            int res = ps.executeUpdate();
            if (log) {
                LOGGER.debug("{} rows affected", res);
            }
            return res;
        }
        catch (SQLException e) {
            throw new JingJdbcException(e);
        }
    }

    public static List<HashMap<String, String>> query(Connection connection, String sql, Object... params) throws JingJdbcException {
        return query(connection, true, sql, params);
    }

    public static List<HashMap<String, String>> query(Connection connection, boolean log, String sql, Object... params) throws JingJdbcException {
        if (log) {
            LOGGER.debug("[query] {}", sql);
        }
        List<HashMap<String, String>> retList = null;
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            setParameters(ps, params);
            ResultSet resultSet = ps.executeQuery();
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            int size$Column = resultSetMetaData.getColumnCount();
            String[] columns = new String[size$Column];
            for (int i$ = 1; i$ <= size$Column; i$++) {
                columns[i$ - 1] = resultSetMetaData.getColumnLabel(i$);
            }
            while (resultSet.next()) {
                if (null == retList) {
                    retList = new ArrayList<>();
                }
                HashMap<String, String> row = new HashMap<>();
                for (int i$ = 1; i$ <= size$Column; i$++) {
                    row.put(columns[i$ - 1], resultSet.getString(i$));
                }
                retList.add(row);
            }
            if (log) {
                LOGGER.debug("{} rows selected", GenericUtil.count(retList));
            }
            return retList;
        }
        catch (SQLException e) {
            throw new JingJdbcException(e);
        }
    }

    public static void setParameters(PreparedStatement ps, Object[] params) throws JingJdbcException {
        int length = GenericUtil.count(params);
        Object param;
        try {
            for (int i$ = 0; i$ < length; i$++) {
                param = params[i$];
                if (param instanceof Short) {
                    ps.setShort(i$ + 1, (Short) param);
                }
                else if (param instanceof Integer) {
                    ps.setInt(i$ + 1, (Integer) param);
                }
                else if (param instanceof Long) {
                    ps.setLong(i$ + 1, (Long) param);
                }
                else if (param instanceof Float) {
                    ps.setFloat(i$ + 1, (Float) param);
                }
                else if (param instanceof Double) {
                    ps.setDouble(i$ + 1, (Double) param);
                }
                else if (param instanceof BigDecimal) {
                    ps.setBigDecimal(i$ + 1, (BigDecimal) param);
                }
                else if (param instanceof String) {
                    ps.setString(i$ + 1, (String) param);
                }
                else {
                    ps.setString(i$ + 1, ClassUtil.cast2String(param));
                }
            }
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingJdbcException(t);
        }
    }

    private static long countSumAffectedRow(int[] affectedRows) {
        int length = null == affectedRows ? 0 : affectedRows.length;
        long sum = 0;
        for (int i$ = 0; i$ < length; i$++) {
            sum += affectedRows[i$];
        }
        return sum;
    }
}
